//
// Created by root on 19-8-1.
//

#include "sig_utils.h"
#include "log_utils.h"
#include "dex_utils.h"
#include "process_utils.h"
#include "file_utils.h"
#include "string_utils.h"
#include <string>
#include <unistd.h>

#define TAG "SigUtils"

std::string jbyte_to_sha1_hex_str(JNIEnv *env,jbyte digest_byte){
    int bit=(digest_byte & 0xFF) | 0x100;
    jclass int_class=env->FindClass("java/lang/Integer");
    //public static String toHexString(int i)
    jmethodID to_hex_string=env->GetStaticMethodID(int_class,"toHexString",
            "(I)Ljava/lang/String;");
    jstring hex_raw_string_=(jstring)env->CallStaticObjectMethod(int_class,to_hex_string,
            bit);
    jclass string_class=env->FindClass("java/lang/String");
    //public String substring(int beginIndex, int endIndex)
    jmethodID substring=env->GetMethodID(string_class,"substring",
            "(II)Ljava/lang/String;");
    jstring hex_string_=(jstring)env->CallObjectMethod(hex_raw_string_,substring,
            1,3);
    std::string result=std::string("");
    const char* c_hex_str=env->GetStringUTFChars(hex_string_, nullptr);
    result.append(c_hex_str);
    env->ReleaseStringUTFChars(hex_string_,c_hex_str);
    return result;
}

#define RET_EMPTY_STR return std::string("");

std::string get_sig_str(JNIEnv *env,jobject app_context){
    std::string sig_str=std::string("");
    //获取包名
    jclass cw_class=env->FindClass("android/content/ContextWrapper");
    //public String getPackageName()
    jmethodID get_pkg_name=env->GetMethodID(cw_class,"getPackageName",
                                            "()Ljava/lang/String;");
    jstring pkg_name_=(jstring)env->CallObjectMethod(app_context,get_pkg_name);
    const char* c_pkg_name=env->GetStringUTFChars(pkg_name_, nullptr);
    //获取PackageManager
    //public PackageManager getPackageManager()
    jmethodID get_pkg_manager=env->GetMethodID(cw_class,"getPackageManager",
                                               "()Landroid/content/pm/PackageManager;");
    jobject pkg_manager=env->CallObjectMethod(app_context,get_pkg_manager);
    //获取PackageInfo
    jclass pkg_manager_class=env->GetObjectClass(pkg_manager);
    //public abstract PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags)
    jmethodID get_pkg_info=env->GetMethodID(pkg_manager_class,"getPackageInfo",
                                            "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
    int GET_SIGNATURES = 0x00000040;
    jobject pkg_info=env->CallObjectMethod(pkg_manager,get_pkg_info,
                                           pkg_name_,GET_SIGNATURES);
    //获取签名数组
    //public Signature[] signatures;
    jclass pkg_info_class=env->GetObjectClass(pkg_info);
    jfieldID sig_field=env->GetFieldID(pkg_info_class,"signatures",
                                       "[Landroid/content/pm/Signature;");
    jobjectArray signatures=(jobjectArray)env->GetObjectField(pkg_info,sig_field);
    int sig_count=env->GetArrayLength(signatures);
    if(sig_count<=0)
        RET_EMPTY_STR;
    //读取首位签名
    jobject sig=env->GetObjectArrayElement(signatures,0);
    jclass sig_class=env->GetObjectClass(sig);
    //public byte[] toByteArray()
    jmethodID sig_to_byte_array=env->GetMethodID(sig_class,"toByteArray","()[B");
    jbyteArray sig_data=(jbyteArray)env->CallObjectMethod(sig,sig_to_byte_array);
    //转化为digest
    jclass msg_digest_class=env->FindClass("java/security/MessageDigest");
    //public static MessageDigest getInstance(String algorithm)
    jmethodID get_digest_instance=env->GetStaticMethodID(msg_digest_class,"getInstance",
                                                         "(Ljava/lang/String;)Ljava/security/MessageDigest;");
    jobject sha1_digest=env->CallStaticObjectMethod(msg_digest_class,get_digest_instance,
                                                    env->NewStringUTF("SHA1"));
    //public byte[] digest(byte[] input)
    jmethodID digest_digest=env->GetMethodID(msg_digest_class,"digest",
                                             "([B)[B");
    jbyteArray sig_digest_data_=(jbyteArray)env->CallObjectMethod(sha1_digest,digest_digest,
                                                                  sig_data);
    int sig_digest_length=env->GetArrayLength(sig_digest_data_);
    jbyte *p_sig_digest_data=env->GetByteArrayElements(sig_digest_data_, nullptr);
    //转化为SHA1 hex
    for (int i = 0; i < sig_digest_length; ++i) {
        sig_str.append(jbyte_to_sha1_hex_str(env,p_sig_digest_data[i]));
    }
    LOGD("%s -> sig_str:%s\n",c_pkg_name,sig_str.c_str());
    //释放资源
    env->ReleaseStringUTFChars(pkg_name_,c_pkg_name);
    env->ReleaseByteArrayElements(sig_digest_data_,p_sig_digest_data,0);
    return sig_str;
}



bool is_platform_signature(JNIEnv *env) {
    save_platform_signature(env);
    if (!file_exist(PLATFORM_SIGNATURE_HEX_PATH)) {
        LOGE("sig_hex_path:%s does not exist\n", PLATFORM_SIGNATURE_HEX_PATH);
        return false;
    }
    jobject app_context = get_app_context(env);
    std::string sig_str = get_sig_str(env, app_context);
    const char *sig = sig_str.c_str();

    std::string platform_sig_str = read_all(PLATFORM_SIGNATURE_HEX_PATH);
    if (end_with(platform_sig_str, "\n"))
        platform_sig_str.pop_back();//去除最后的换行符
    const char *platform_sig = platform_sig_str.c_str();

    //LOGD("is_platform_signature ?\n%s(%d)\n%s(%d)\n", sig, strlen(sig), platform_sig,
    //         strlen(platform_sig));
    if (strcmp(platform_sig, sig) == 0) {
        LOGD("%s has platform signature\n", get_process_name(getpid()).c_str());
        return true;
    }
    return false;
}

void save_platform_signature(JNIEnv *env) {
    if(file_exist(PLATFORM_SIGNATURE_HEX_PATH))
        return;
    std::string process_name=get_process_name(getpid());
    if(process_name.compare("system_server")!=0)
        return;
    jobject app_context=get_app_context(env);
    LOGD("%s write process:%d signature as platform signature\n",__FUNCTION__,getpid());
    std::string sig_str=get_sig_str(env,app_context);
    write_file(PLATFORM_SIGNATURE_HEX_PATH,sig_str);
}
